package chipyard.fpga.arty

import chipyard.TestHarness
import chisel3._
import freechips.rocketchip.devices.debug._
import freechips.rocketchip.jtag.JTAGIO
import freechips.rocketchip.subsystem._
import sifive.blocks.devices.uart._
import sifive.blocks.devices.jtag._
import sifive.blocks.devices.pinctrl._
import sifive.fpgashells.ip.xilinx.{IBUFG, IOBUF, PULLUP, PowerOnResetFPGAOnly}
import chipyard.harness.{ComposeHarnessBinder, OverrideHarnessBinder}
import chipyard.iobinders.JTAGChipIO
import sifive.blocks.devices.spi.{HasPeripherySPIModuleImp, SPIPortIO}

class WithArtyResetHarnessBinder extends ComposeHarnessBinder({
  (system: HasPeripheryDebugModuleImp, th: ArtyFPGATestHarness, ports: Seq[Bool]) => {
    require(ports.size == 2)

    withClockAndReset(th.clock_32MHz, th.ck_rst) {
      // Debug module reset
      th.dut_ndreset := ports(0)

      // JTAG reset
      ports(1) := PowerOnResetFPGAOnly(th.clock_32MHz)
    }
  }
})

class WithArtyJTAGHarnessBinder extends OverrideHarnessBinder({
  (system: HasPeripheryDebug, th: ArtyFPGATestHarness, ports: Seq[Data]) => {
    ports.map {
      case j: JTAGChipIO =>
        withClockAndReset(th.buildtopClock, th.hReset) {
          val jtag_wire = Wire(new JTAGIO)
          jtag_wire.TDO.data := j.TDO
          jtag_wire.TDO.driven := true.B
          j.TCK := jtag_wire.TCK
          j.TMS := jtag_wire.TMS
          j.TDI := jtag_wire.TDI

          val io_jtag = Wire(new JTAGPins(() => new BasePin(), false)).suggestName("jtag")

          JTAGPinsFromPort(io_jtag, jtag_wire)

          io_jtag.TCK.i.ival := IBUFG(IOBUF(th.jd_2).asClock).asBool

          IOBUF(th.jd_5, io_jtag.TMS)
          PULLUP(th.jd_5)

          IOBUF(th.jd_4, io_jtag.TDI)
          PULLUP(th.jd_4)

          IOBUF(th.jd_0, io_jtag.TDO)

          // mimic putting a pullup on this line (part of reset vote)
          th.SRST_n := IOBUF(th.jd_6)
          PULLUP(th.jd_6)

          // ignore the po input
          io_jtag.TCK.i.po.map(_ := DontCare)
          io_jtag.TDI.i.po.map(_ := DontCare)
          io_jtag.TMS.i.po.map(_ := DontCare)
          io_jtag.TDO.i.po.map(_ := DontCare)
        }
    }
  }
})

class WithArtyUARTHarnessBinder extends OverrideHarnessBinder({
  (system: HasPeripheryUARTModuleImp, th: ArtyFPGATestHarness, ports: Seq[UARTPortIO]) => {
    withClockAndReset(th.clock_32MHz, th.ck_rst) {
      IOBUF(th.uart_txd_in,  ports.head.txd)
      ports.head.rxd := IOBUF(th.uart_rxd_out)
    }
  }
})

//class WithArtySPIHarnessBinder extends OverrideHarnessBinder({
//  (system: HasPeripherySPIModuleImp, th: ArtyFPGATestHarness, ports: Seq[SPIPortIO]) => {
////    IOBUF(th.qspi_cs, ports.head.cs.head)
////    IOBUF(th.qspi_sck, ports.head.sck)
////    IOBUF(th.qspi_dq.head, ports.head.dq.head.i)
////    IOBUF(th.qspi_dq(1), ports.head.dq(1).i)
//    //IOBUF(th.qspi_dq, ports.head.dq)
////    val spiFlashModel = th.spiFlashModel
//    ports.zipWithIndex.foreach { case (port, index) =>
//      // 控制 SPI 数据线的方向
//      // 这里假设 ie 和 oe 分别控制输入使能和输出使能
//      // ie 高电平时使能输入，oe 高电平时使能输出
//      port.cs.foreach { cs => // 假设片选信号是低电平有效
//        //withClockAndReset(th.buildtopClock, th.dutReset) {
//        IOBUF(th.qspi_sck, ports.head.sck)
//        IOBUF(th.qspi_cs, ports.head.cs.head)
//        port.dq.foreach { bit =>
//          bit.i := IOBUF(th.btn_0)
//          IOBUF(th.btn_1, bit.o)
//          IOBUF(th.btn_2, bit.ie)
//          IOBUF(th.btn_3, bit.oe)
//        }
//      }
//    }
//  }
//})
class WithArtySPIHarnessBinder extends OverrideHarnessBinder({
  (system: HasPeripherySPIModuleImp, th: ArtyFPGATestHarness, ports: Seq[SPIPortIO]) => {
    //    IOBUF(th.qspi_cs, ports.head.cs.head)
    //    IOBUF(th.qspi_sck, ports.head.sck)
    //    IOBUF(th.qspi_dq.head, ports.head.dq.head.i)
    //    IOBUF(th.qspi_dq(1), ports.head.dq(1).i)
    //IOBUF(th.qspi_dq, ports.head.dq)
    //    val spiFlashModel = th.spiFlashModel
    IOBUF(th.qspi_sck, ports.head.sck)
    IOBUF(th.qspi_cs, ports.head.cs.head)
    val btnSignals = Seq(th.btn_0, th.btn_1, th.btn_2, th.btn_3,th.btn_4, th.btn_5, th.btn_6, th.btn_7,th.btn_8, th.btn_9, th.btn_10, th.btn_11,th.btn_12, th.btn_13, th.btn_14, th.btn_15)
    ports.zipWithIndex.foreach { case (port, index) =>
        port.dq.zipWithIndex.foreach { case (bit, bitIndex) =>
//          bit.i := IOBUF(th.btn_0)
//          IOBUF(th.btn_1, bit.o)
//          IOBUF(th.btn_2, bit.ie)
//          IOBUF(th.btn_3, bit.oe)


          val btnIndex = bitIndex * 4
          println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   index: " + index)
          println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   bitIndex: " + bitIndex)
          println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@    btnIndex: " + btnIndex)
          bit.i := IOBUF(btnSignals(btnIndex))
          //println("btnIndex1: " + (btnIndex + 1)
          IOBUF(btnSignals(btnIndex + 1), bit.o)
          //println("btnIndex2: " + ((btnIndex + 2) % 4))
          IOBUF(btnSignals(btnIndex + 2), bit.ie)
          //println("btnIndex3: " + ((btnIndex + 3) % 4))
          IOBUF(btnSignals(btnIndex + 3), bit.oe)
        }
    }
  }
})